#ifndef __ALSA_WRAPPER_H
#define __ALSA_WRAPPER_H

#pragma once

#include "asoundlib.h"
#include "RingBuffer.h"
#include "SoundManager.h"

// Definitions for Microsoft WAVE format
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define COMPOSE_ID(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24))
#define LE_SHORT(v) (v)
#define LE_INT(v)   (v)
#define BE_SHORT(v) bswap_16(v)
#define BE_INT(v)   bswap_32(v)
#elif __BYTE_ORDER == __BIG_ENDIAN
#define COMPOSE_ID(a,b,c,d) ((d) | ((c)<<8) | ((b)<<16) | ((a)<<24))
#define LE_SHORT(v) bswap_16(v)
#define LE_INT(v)   bswap_32(v)
#define BE_SHORT(v) (v)
#define BE_INT(v)   (v)
#else
#error "Wrong endian"
#endif

#define WAV_RIFF COMPOSE_ID('R','I','F','F')
#define WAV_WAVE COMPOSE_ID('W','A','V','E')
#define WAV_FMT  COMPOSE_ID('f','m','t',' ')
#define WAV_DATA COMPOSE_ID('d','a','t','a')

// WAVE fmt block constants from Microsoft mmreg.h header
#define WAV_FMT_PCM 		0x0001
#define WAV_FMT_IEEE_FLOAT 	0x0003

#define _(msgid) gettext (msgid)
#define gettext_noop(msgid) msgid
#define N_(msgid) gettext_noop (msgid)

#ifndef LLONG_MAX
#define LLONG_MAX 				9223372036854775807LL
#endif

#define DEFAULT_FORMAT 			SND_PCM_FORMAT_S16_LE
#define DEFAULT_SPEED 			8000
#define DEFAULT_RATE			16000 // 44100 // 16000
#define DEFAULT_CHANNELS		1 // 2 // 1
#define FORMAT_DEFAULT 			-1
#define FORMAT_RAW 				0
#define FORMAT_VOC 				1
#define FORMAT_WAVE 			2
#define FORMAT_AU 				3
#define DEFAULT_CHUNK_SIZE		2000
#define RING_BUFFER_SIZE 		65536
#define AUDIO_BUFFER_SIZE		2048
#define FULL_AUDIO_BUFFER_SIZE	524288

#ifdef SND_CHMAP_API_VERSION
#define CONFIG_SUPPORT_CHMAP	1
#endif

#ifndef LLONG_MAX
#define LLONG_MAX    			9223372036854775807LL
#endif

#ifndef le16toh
#include <asm/byteorder.h>
#define le16toh(x) 				__le16_to_cpu(x)
#define be16toh(x) 				__be16_to_cpu(x)
#define le32toh(x) 				__le32_to_cpu(x)
#define be32toh(x) 				__be32_to_cpu(x)
#endif

#define FILTERSWEEP_LFO_CENTER 	2000.
#define FILTERSWEEP_LFO_DEPTH 	1800.
#define FILTERSWEEP_LFO_FREQ 	0.2
#define FILTER_BANDWIDTH 		50

enum
{
	OPT_VERSION = 1,
	OPT_PERIOD_SIZE,
	OPT_BUFFER_SIZE,
	OPT_DISABLE_RESAMPLE,
	OPT_DISABLE_CHANNELS,
	OPT_DISABLE_FORMAT,
	OPT_DISABLE_SOFTVOL,
	OPT_TEST_POSITION,
	OPT_TEST_COEF,
	OPT_TEST_NOWAIT,
	OPT_MAX_FILE_TIME,
	OPT_PROCESS_ID_FILE,
	OPT_USE_STRFTIME,
	OPT_DUMP_HWPARAMS,
	OPT_FATAL_ERRORS,
};

enum
{
	VUMETER_NONE,
	VUMETER_MONO,
	VUMETER_STEREO
};

class AlsaWrapper: public SoundManager
{
public:
	AlsaWrapper();
	virtual ~AlsaWrapper();

    virtual int play(char *filename);	// Implementation of play method
    virtual int record(char *filename);	// Implementation of record method
	virtual int setparams();			// Setup parameters

    int playraw(char *filename);		// Play raw PCM file
    void playbackraw(char *name);		// Control raw PCM file play
    int playrawbuffer(unsigned char * buffer, int size);
    									// Play raw PCM sound buffer

    int startAudioCapture();			// Start the audio streaming
    int getAudioStream();				// Get the audio output stream
    int endAudioCapture();				// End the audio streaming

    void usage(char *command);			// Display library usage info
    void device_list(void);				// Display device list
    void pcm_list(void);				// Display PCM list
    void version(void);					// Display file version

    static void begin_voc(int fd, size_t cnt);
    									// Begin VOC format
    static void begin_wave(int fd, size_t cnt);
    									// Begin WAV format
    static void begin_au(int fd, size_t cnt);
    									// Begin AU format

    static void end_voc(int fd);		// End VOC format
    static void end_wave(int fd);		// End WAV format
    static void end_au(int fd);			// End EU format

    static void init_stdin(void);		// Standard IN initialized
    static void done_stdin(void);		// Standard IN shutdown

    static void signal_handler_recycle(int sig);
    									// Signal handler recycle
    static void signal_handler(int sig);
    									// Signal handler

    long readbuf(snd_pcm_t *handle, char *buf,
    			 long len, size_t *frames, size_t *max);
    									// Read from the PCM buffer

    long writebuf(snd_pcm_t *handle, char *buf,
    			 long len, size_t *frames);
    									// Write to the PCM buffer

	// Play back raw PCM data
    void playbackv_go(int* fds, unsigned int channels, size_t loaded,
     				  off64_t count, int rtype, char **names);

	// Capture raw PCM data
    void capturev_go(int* fds, unsigned int channels,
    				 off64_t count, int rtype, char **names);

	// Capture raw data
    void capturev(char **names, unsigned int count);

	// Play back raw data
    void playbackv(char **names, unsigned int count);

	// Set the parameters for streaming
    int setparams_stream(snd_pcm_t *handle,
    					 snd_pcm_hw_params_t *params,
    					 const char *id);

	// PCM read function
    ssize_t pcm_read(unsigned char *data, size_t rcount);

#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
#define error(...) \
do \
{ \
	fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
	fprintf(stderr, __VA_ARGS__); \
	putc('\n', stderr); \
} \
while (0)
#else
#define error(args...) \
do \
{ \
	fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
	fprintf(stderr, ##args); \
	putc('\n', stderr); \
} \
while (0)
#endif

#ifndef check_wavefile_space
#define check_wavefile_space(buffer, len, blimit) \
	if (len > blimit) { \
		blimit = len; \
		if ((buffer = (unsigned char *)realloc(buffer, blimit)) == NULL) { \
			error(_("not enough memory")); \
			prg_exit(EXIT_FAILURE); \
		} \
	}
#endif

#ifndef timersub
#define timersub(a, b, result) \
		do { \
			(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
			(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
			if ((result)->tv_usec < 0) { \
				--(result)->tv_sec; \
				(result)->tv_usec += 1000000; \
			} \
		} while (0)
#endif

private:
	int 				timelimit;		// Record time limit
	int 				file_type;		// Type of sound file
	int 				open_mode;		// Mode to open PCM sound handle with
	int 				mmap_flag;		// Memory map access flag
	int 				interleaved;	// Interleave forward error correction flag
	int					block;			// Blocking mode flag
	int 				nonblock;		// Nonblocking (anti-blocking) flag
	unsigned char      *audiobuf;		// Audio buffer pointer
	snd_pcm_uframes_t 	chunk_size;		// Size of an audio chunk
	size_t 				chunk_bytes;	// Chunk byte size
	unsigned 			period_time;	// PCM period time
	unsigned 			buffer_time;	// Amount of calculated time allocated in the buffer
	snd_pcm_uframes_t 	period_frames;	// Period time frames
	snd_pcm_uframes_t 	buffer_frames;	// Buffer time frames
	int 				avail_min;		// Minimum available
	int 				start_delay;	// Start play delay
	int 				stop_delay;		// Delay after stop
	int 				monotonic;		// Mono flag
	int 				fatal_errors;	// Fatal error flag
	int 				vumeter;		// VU meter quantity
	int 				buffer_pos;		// Position in the buffer
	size_t 				bits_per_sample;// Bits-per-sample
	size_t 				bits_per_frame; // Bits-per-frame
	int 				test_position;	// Flag to test buffer position or not
	int 				test_coef;		// Coefficient for calculating buffer size (8)
	long long 			max_file_size;	// Maximum WAV file size
	int 				max_file_time;	// Maximum time a file has available to store data
	int 				use_strftime;	// Use time tagging flag
	int 				dump_hw_params;	// Dump Hardware parameters flag
	off64_t 			pbrec_count;	// Play Back record count
	int 				vocmajor;		// VOC file major Id marker
	int 				vocminor;		// VOC file minor Id marker
	int 				ringBufferSize;	// Ring buffer size
	int 				resample;		// Re-sample flag
	snd_pcm_uframes_t	period_size;	// PCM Period size
	int					buffer_size;	// Size of stream buffer
	int 				latency_min;	// In frames / 2
	int					latency_max;	// Maximum latency setting
	snd_output_t	   *output;			// Output buffer
	unsigned long 		loop_limit;		// Loop limit
	int 				loop_sec;		// Loop seconds
	int 				effect;			// Use Effects flag
	RingBuffer		   *ringbuffer;		// Ring buffer handle
	pthread_t 			thread;			// Read sound buffer thread
	pthread_attr_t 		attr;			// Thread attributes

#ifdef CONFIG_SUPPORT_CHMAP
	snd_pcm_chmap_t	   *channel_map; 	// Character map to override
	unsigned int	   *hw_map; 	 	// Character map to folset_play_paramslow
#endif

    ssize_t safe_read(int fd, void *buf, size_t count);
    									// THread safe read

    int test_vocfile(void *buffer);		// Test if VOC file
    ssize_t test_wavefile(int fd, unsigned char *_buffer, size_t size);
    									// Test if WAV file
    size_t test_wavefile_read(int fd, unsigned char *buffer, size_t *size, size_t reqsize, int line);
    									// Test if can read a WAV file
    int test_au(int fd, void *buffer);	// Test if AU file

    void show_available_sample_formats(snd_pcm_hw_params_t* params);
    									// Show sample formats
    int setup_chmap(void);				// Setup character map
    void set_play_params(void);			// Setup play parameters
    void set_record_params(void);		// Setup record parameters

    void xrun_play(void);				// Handle buffer play over/under run
    void capture(char *orig_name);		// Capture voice audio to wave file
    void stop(void);					// Stop recording process

    void print_vu_meter_mono(int perc, int maxperc);
    									// Display mono VU meter text line
    void print_vu_meter_stereo(int *perc, int *maxperc);
    									// Display stereo VU meter text line
    void print_vu_meter(signed int *perc, signed int *maxperc);
    									// Display meter text line
    void compute_max_peak(unsigned char *data, size_t count);
    									// Peak handler

    void do_test_position(void);		// Test buffer position

    unsigned char* remap_data(unsigned char *data, size_t count);
    									// Re-map data in memory
    unsigned char** remap_datav(unsigned char **data, size_t count);
    									// Re-map data for all channels

    ssize_t pcm_write(unsigned char *data, size_t count);
    									// PCM write function
    ssize_t pcm_writev(unsigned char **data, unsigned int channels, size_t count);
    									// PCM write channels function
    ssize_t pcm_readv(unsigned char **data, unsigned int channels, size_t rcount);
    									// PCM read channels function

    ssize_t voc_pcm_write(unsigned char *data, size_t count);
    									// VOC PCM write function
    void voc_write_silence(unsigned x);
    									// VOC Write silence
    void voc_pcm_flush(void);
    									// VOC PCM flush
    void voc_play(int fd, int ofs, char *name);
    									// VOC play
    void init_raw_data(void);
    									// Initialize for RAW data

    off64_t calc_count(void);			// Calculate the data count to read from/to dsp
    void header(int rtype, char *name); // Create wave file header

    void playback_go(int fd, size_t loaded, off64_t count,
    				 int rtype, char *name);
   										// Play raw data
    void playback(char *name);			// Play wav file

	// Time string formatting method
    size_t mystrftime(char *s, size_t max, const char *userformat,
	  	  	  	  	  const struct tm *tm, const int filenumber);

	// Create a new file to record to
    int new_capture_file(char *name, char *namebuf,
    					 size_t namelen, int filecount);

	// Create and display new file time
    int new_capture_file_strftime(char *name, char *namebuf,
    					 size_t namelen, int filecount);

    int create_path(const char *path);	// Creates a file path, like mkdir -p
    int safe_open(const char *name);	// Thread safe open

	// Set the streaming buffer size
    int setparams_bufsize(snd_pcm_t *handle,
    					  snd_pcm_hw_params_t *params,
    					  snd_pcm_hw_params_t *tparams,
    					  snd_pcm_uframes_t bufsize,
    					  const char *id);

    // Set parameters
    int setparams_set(snd_pcm_t *handle,
    				  snd_pcm_hw_params_t *params,
    				  snd_pcm_sw_params_t *swparams,
    				  const char *id);

    // Set streaming parameters
    int set_stream_params(snd_pcm_t *phandle,
    			  	     snd_pcm_t *chandle,
    			  	     int *bufsize);

    // Show statistics
    void showstat(snd_pcm_t *handle, size_t frames);
    // Show latency
    void showlatency(size_t latency);
    // Show IN max
    void showinmax(size_t in_max);
    // Get time stamp
    void gettimestamp(snd_pcm_t *handle, snd_timestamp_t *timestamp);
    // Set the buffer type
    void setscheduler(void);
    // Calculate time difference
    long timediff(snd_timestamp_t t1, snd_timestamp_t t2);
    // Apply special effects
    void applyeffect(char* buffer, int r);
};

#endif
